/*
 * File:   mcFrustum.h
 * Author: Thales Luis
 *
 * Created on September 30, 2009, 5:33 PM
 */


/// Evita problemas de linking caso o opengl não esteja instalado ou o usuário
/// não queira usar o opengl
#ifdef OPENGL
    #include <GL/gl.h>
    #include <GL/glu.h>
#endif

#include <math.h>
#include <stdio.h>
#include <iostream>

#include "mcMath.h"

namespace mc {

    typedef enum {
        MC_FRUSTUM_PARTIAL,
        MC_FRUSTUM_OUTSIDE,
        MC_FRUSTUM_INSIDE
    } point_pos ;


    class Frustum {
          public: // But read-only
            // CAMERA POSITION AND ORIENTATION: in world coordinates
            float   x, y, z;    // Observer's position

            vec3<float> view;     // Viewing direction: given by the quaternion
            vec3<float> up;       // Head up direction: given by the quaternion
            vec3<float> right;    // Right side direction: given by the quaternion

          private:
            // CAMERA PARAMETERS (always updated by changes in frustum)
            quaternion<float> orientation; // QUATERNION reflecting the viewers orientation.
            matrix4<float>    view_matrix;

            // PARAMETROS DA PROJECAO
            matrix4<float> proj_matrix;

            // Equacoes dos planos do FRUSTUM corrente (redundante pois é calculado toda hora)
            // Coeficientes do plano superior do frustum
            float topA, topB, topC, topD;

            // Coeficientes do plano inferior do frustum
            float bottomA, bottomB, bottomC, bottomD;

            // Coeficientes do plano posterior do frustum
            float farA, farB, farC, farD;

            // Coeficientes do plano anterior do frustum
            float nearA, nearB, nearC, nearD;

            // Coeficientes do plano direito do frustum
            float rightA, rightB, rightC, rightD;

            // Coeficientes do plano esquerdo do frustum
            float leftA, leftB, leftC, leftD;

          public:
            //metodos construtores
            Frustum();
            ~Frustum();

            ///////////////////////////////////////////////////////////////////////
            // FREE VIEWER

            // Posicao do observador
            void setPosition(float x, float y, float z); // Posicao do observador
            void movePosition(float dx, float dy, float dz);   // Move o observador, dado um vetor
            void advancePosition(float forward, float upward, float rightward); // Avanca usando as direcoes de visualizacao

            // Direcoes de observacao
            void resetOrientation();    // Loads the canonical reference system
            void setViewVector(float dirX, float dirY, float dirZ); // Configura vetor de direcao
            void setViewVectorSpherical(float latit, float longit); // latit em [-90, 90]  longit em [-180, 180]
            void setUpVector(float cimaX, float cimaY, float cimaZ); // Configura vetor acima

            void setTarget(float alvoX, float alvoY, float alvoZ); // Ajusta as direcoes para visualizar o alvo.

            ///////////////////////////////////////////////////////////////////////
            // OBSERVER IN THE CENTER OF AN UNITARY SPHERE (planetarium)

            // Rotates the unit ball where the observer is centered (planetarium)
            //  - all functions use the local viewing system to apply rotations
            void rotateOrientationTrackball(float x1, float y1, float x2, float y2); // Trackball: p1,p2 em [-1,1]x[-1,1] plano no qual a bola é projetada. rotacao livre.
            void rotateOrientationSpherical(float dlat, float dlong);       // Free rotation of the planetarium using spherical angles
            void rotateOrientationEuler(float pitch, float yaw, float row); // Rotation of the planetarium using Euler angles but keeping main axis aligned with Y e X
            void rotateOrientationAxis(float ang, float axisX, float axisY, float axisZ); // Rotation of the planetarium around a given axis.
            void alignAxis(); // Aligns up vector with Y, and right vector with X

            ///////////////////////////////////////////////////////////////////////
            // OBSERVER IS FREE ROTATING AROUND AN ARBITRARY SPHERE (orbital)

            // Rotation of the observer's system around a sphere
            //  - all functions align the local viewing system to the canonical system,
            //    apply rotations and then align the canonical system back to the local system
            void rotateOrbitTrackball(float centroX, float centroY, float centroZ, float x1, float y1, float x2, float y2); // Trackball: p1,p2 em [-1,1]x[-1,1] plano no qual a esfera é projetada. rotacao livre.
            void rotateOrbitSpherical(float centroX, float centroY, float centroZ, float dlat, float dlong); // Spherical coordinates
            void rotateOrbitEuler(float centroX, float centroY, float centroZ, float pitch, float yaw, float roll); // Euler angles


            ///////////////////////////////////////////////////////////////////////
            // OBSERVER IS FREE ROTATING AROUND ARBITRARY AXIS

            // Rotation of the observer's system around an axis
            void rotateAxis(float ang, float ox, float oy, float oz, float dirX, float dirY, float dirZ);


            ///////////////////////////////////////////////////////////////////////
            // VISIBILITY TESTS

            point_pos isPointVisible(float x, float y, float z );
            point_pos isSphereVisible(float x, float y, float z, float raio);
            point_pos isBoxVisible(float xmin, float ymin, float zmin, float xmax, float ymax, float zmax);

            ///////////////////////////////////////////////////////////////////////
            //  GEOMETRIC PROJECTIONS
            void setPerspective(float fovh, float aspect, float prox, float dist);
            void setOrthographic(float left, float right, float top, float bottom, float near, float far);

            ///////////////////////////////////////////////////////////////////////
            //  GP Interfaces

            #ifdef OPENGL
                // Loads the projection and modelview matrix to OpenGL
                void exportOpenGL();
            #endif

            ///////////////////////////////////////////////////////////////////////
            //  CAMERA INFORMATION RETRIEVAL

            void getViewMatrix( matrix4<float> matrot );
            void getProjectionMatrix( matrix4<float> matproj );

          private:
            // Calcula as direcoes do observador
            void computeParameters();

            void simulateTrackBall( 
                quaternion<float> &quat,
                float p1x,
                float p1y,
                float p2x,
                float p2y
            );

            /**
             * Código de Gavin Bell
             * Projeta um par (x,y) em uma esfera de raio r OR uma folha hiperbólica
             * se estivermos longe do centro da esfera.
             */
            inline float tb_project_to_sphere( float r, float x, float y );
    };

};

